﻿using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Common
{
    /// <summary>
    /// Tcp服务端 宿主
    /// </summary>
    public class TcpHostWorker : BackgroundService
    {
        private readonly Logger _logger;
        private readonly IServiceProvider _serviceProvider;

        internal TcpClient TcpHost { get; set; }

        private EndPoint _RemoteEndPoint = null;

        private NetworkStream _NetworkStream = null;

        /// <summary>
        /// 断开连接
        /// </summary>
        public event Action<TcpHostWorker, CancellationToken> UnConnected;

        /// <summary>
        /// 已经连接
        /// </summary>
        public event Action<TcpHostWorker, CancellationToken> Connectionned;

        /// <summary>
        /// 接收到消息
        /// </summary>
        public event Action<Message, CancellationToken> ReceiveMessage;

        /// <summary>
        /// 宿主唯一标识
        /// </summary>
        public String HostWorkerID
        {
            get
            {
                return _RemoteEndPoint.ToString();
            }
        }

        /// <summary>
        /// 远程主机IP
        /// </summary>
        public String IPAddress
        {
            get
            {
                return (_RemoteEndPoint as IPEndPoint).Address.ToString();
            }
        }

        /// <summary>
        /// 远程主机Port
        /// </summary>
        public int Port
        {
            get
            {
                return (_RemoteEndPoint as IPEndPoint).Port;
            }
        }


        /// <summary>
        /// 结束标记
        /// </summary>
        public byte LineOff { get; set; } = (int)'\n';

        /// <summary>
        /// 检查结束符位置
        /// </summary>
        public event Func<byte[], int> LineOffCheck;

        /// <summary>
        /// 接收缓冲区大小
        /// </summary>
        public int BufferSize { get; set; } = 1024 * 1000;

        /// <summary>
        /// 监听获取或者写入的超时时间，默认：3秒
        /// </summary>
        public int Timeout { get; set; } = 3 * 1000;

        /// <summary>
        /// 接收队列大小
        /// </summary>
        public int QueueSize { get; set; } = 0;

        /// <summary>
        /// 通信数据消息队列
        /// </summary>
        private ConcurrentQueue<Message> _concurrentQueue = new ConcurrentQueue<Message>();

        /// <summary>
        /// 是否关闭
        /// </summary>
        bool isClose = true;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="serviceProvider"></param>
        public TcpHostWorker(Logger logger, IServiceProvider serviceProvider)
        {
            _logger = logger;
            _serviceProvider = serviceProvider;
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            try
            {
                _RemoteEndPoint = TcpHost.Client.RemoteEndPoint;
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    TcpHost.Client.IOControl(IOControlCode.KeepAliveValues, GetKeepAliveData(), null);
                }
                TcpHost.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                //TcpHost.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.TcpKeepAliveTime, 3000);
                //TcpHost.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.TcpKeepAliveInterval, 1000);
                TcpHost.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.TcpKeepAliveRetryCount, 3);

                //获取当前连接流对象
                _NetworkStream = TcpHost.GetStream();
                _NetworkStream.ReadTimeout = 3 * 1000;

                //已连接
                Connectionned?.Invoke(this, cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.Error($"服务器异常，异常信息：", ex);
                return Task.CompletedTask;
            }

            _logger.Info($"客户端{_RemoteEndPoint}已连接。");
            isClose = false;
            ReceiveAsync(cancellationToken);
            return base.StartAsync(cancellationToken);
        }

        /// <summary>
        /// 执行过程
        /// </summary>
        /// <param name="stoppingToken"></param>
        /// <returns></returns>
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            //分包拆包
            byte[] tempCacher = Array.Empty<byte>();//下一帧的数据
            byte[] current = Array.Empty<byte>();//当前帧数据，当前帧最后一段
            byte[] bData = Array.Empty<byte>();//接收到的数据
            byte[] buffer = new byte[BufferSize];//接收缓存

            var lineOffIndex = -1;//结束符号的index
            var length = -1;//接收到的数据长度
            var contentLength = 0;//接收到单挑数据的总长度

            try
            {
                while (!stoppingToken.IsCancellationRequested   //检查被取消
                    && isClientConnected(TcpHost))//检查被断开
                //while (!stoppingToken.IsCancellationRequested   //检查被取消
                //&& !((TcpHost.Client.Poll(500, SelectMode.SelectRead) && (TcpHost.Client.Available == 0)) || !TcpHost.Client.Connected))//检查被断开
                {
                    try
                    {
                        lineOffIndex = -1;
                        length = -1;

                        //初始缓存
                        tempCacher = Array.Empty<byte>();
                        contentLength = 0;
                        GC.Collect();

                        while ((length = await _NetworkStream.ReadAsync(buffer, 0, buffer.Length, stoppingToken)) > 0)
                        {
                            if (stoppingToken.IsCancellationRequested)
                            {
                                break;
                            }

                            //当前帧接收到的数据
                            Array.Resize(ref bData, length);// new byte[length];
                            Buffer.BlockCopy(buffer, 0, bData, 0, length);
                            //续接数据
                            _logger.Debug($"接收到数据 长度 => {bData.Length} 数据：【{bData.ToHexString()}】", IPAddress);
                            tempCacher = tempCacher.Concat(bData).ToArray();

                            while (!stoppingToken.IsCancellationRequested && tempCacher.Length > 0)
                            {
                                //在数据内容里面查找结束符
                                lineOffIndex = LineOffCheck == null ? Array.FindIndex(tempCacher, o => o == this.LineOff) : (int)LineOffCheck?.Invoke(tempCacher); //结束符索引下标
                                _logger.Info("结束符索引位置 => " + lineOffIndex, IPAddress);
                                //找到结束符，则将数据根据结束符号位置分包，然后跳出循环
                                if (lineOffIndex >= 0)
                                {
                                    //当前帧的数据长度
                                    contentLength = lineOffIndex + 1;

                                    if (contentLength > tempCacher.Length)
                                    {
                                        //跳过此时数据不完整
                                        break;
                                    }

                                    _logger.Debug($"数据帧长度: {contentLength}", IPAddress);

                                    var message = new Message();
                                    //设置剩余长度
                                    message.LineLength = lineOffIndex;
                                    //当前帧的完整数据
                                    message.Content = new byte[contentLength];
                                    Buffer.BlockCopy(tempCacher, 0, message.Content, 0, contentLength);

                                    //缓存下一帧的开头数据
                                    tempCacher = tempCacher.Skip(contentLength).ToArray();

                                    //没有获取到数据
                                    if (message.Content == null || message.Content.LongLength <= 0)
                                    {
                                        //处理下一帧
                                        continue;
                                    }

                                    message.NetworkStream = _NetworkStream;
                                    message.RemoteEndPoint = _RemoteEndPoint;

                                    //放入队列
                                    _concurrentQueue.Enqueue(message);
                                    //检查是否超出缓存数量，0：不限制
                                    if (QueueSize > 0 && _concurrentQueue.Count > QueueSize)
                                    {
                                        _logger.Debug($"数据队列已满，正在清理", IPAddress);
                                        //移除最久的
                                        _concurrentQueue.TryDequeue(out message);
                                    }
                                }
                                else
                                {
                                    //不完整，则将新的数据拼接到缓存的数据帧中，继续接收数据
                                    break;
                                }
                            }
                        }

                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"数据接收出现异常：【{_RemoteEndPoint}】异常信息 1：", ex);
                        //_NetworkStream.WriteByte(0x00);//发送一个字节的数据检测连接是否断开
                        //await _NetworkStream.FlushAsync(stoppingToken);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Error($"数据接收出现异常：【{_RemoteEndPoint}】异常信息 2：", ex);
            }

            await this.StopAsync(stoppingToken);
            GC.Collect();
        }

        /// <summary>
        /// 停止
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override Task StopAsync(CancellationToken cancellationToken)
        {
            Stop(cancellationToken);
            return base.StopAsync(cancellationToken);
        }

        /// <summary>
        /// 停止监听
        /// </summary>
        public void Stop(CancellationToken cancellationToken)
        {
            isClose = true;
            if (_NetworkStream != null)
            {
                _NetworkStream.Close();
                _NetworkStream.Dispose();
            }
            _NetworkStream = null;
            if (TcpHost != null)
            {
                TcpHost.Close();
                TcpHost.Dispose();
            }
            TcpHost = null;

            _logger.Info($"客户端{_RemoteEndPoint}已断开！");
            //断开连接
            UnConnected?.Invoke(this, cancellationToken);
        }


        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message"></param>
        public async void Send(byte[] bytes, CancellationToken cancellationToken)
        {
            if (isClose)
            {
                await StartAsync(cancellationToken);
            }
            if (cancellationToken.IsCancellationRequested)
            {
                return;
            }
            if (_NetworkStream != null)
            {
                _logger.Debug($"当前服务器向客户端{_RemoteEndPoint}发送数据：{bytes.ToHexString()}");
                await _NetworkStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
                await _NetworkStream.FlushAsync(cancellationToken);
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message"></param>
        public async void Send(Message message, CancellationToken cancellationToken)
        {
            if (isClose)
            {
                await StartAsync(cancellationToken);
            }
            if (cancellationToken.IsCancellationRequested)
            {
                return;
            }
            if (_NetworkStream != null)
            {
                _logger.Debug($"当前服务器向客户端{_RemoteEndPoint}发送数据：{message.Content}");
                await _NetworkStream.WriteAsync(message.Content, 0, message.Content.Length, cancellationToken);
                await _NetworkStream.FlushAsync(cancellationToken);
            }
        }

        /// <summary>
        /// 处理接收到的数据，内部消费队列
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private Task ReceiveAsync(CancellationToken cancellationToken)
        {
            return Task.Factory.StartNew(() =>
            {
                //没有被关闭，并且没有取消
                while (!isClose && !cancellationToken.IsCancellationRequested)
                {
                    //没有接收到数据
                    if (_concurrentQueue.IsEmpty)
                    {
                        continue;
                    }

                    //没有消费数据
                    if (!_concurrentQueue.TryDequeue(out var message))
                    {
                        continue;
                    }

                    //数据长度为0的跳过
                    if (message.Content.Length == 0)
                    {
                        continue;
                    }

                    try
                    {
                        //触发事件
                        ReceiveMessage?.Invoke(message, cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        _logger.Debug($"数据处理出现异常，【{_RemoteEndPoint}】异常信息：", ex);
                    }
                }
            }, cancellationToken);
        }

        /// <summary>
        /// 获取KeepAliveData
        /// keep-alive每3秒发送一次。如果对方没有响应，每0.5秒后发送一次确认，如果连续3次没有回应，连接会自动变成TcpState.Established
        /// </summary>
        /// <returns></returns>
        private byte[] GetKeepAliveData()
        {
            uint dummy = 0;
            byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
            BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);//启用KeepAlive
            BitConverter.GetBytes((uint)3000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//keep-alive间隔，没用数据传输时，自动发送探测包，每三秒一次
            BitConverter.GetBytes((uint)500).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);// 探测包发送后的等待尝试间隔，半秒
            return inOptionValues;
        }

        /// <summary>
        /// THIS FUNCTION WILL CHECK IF CLIENT IS STILL CONNECTED WITH SERVER.
        /// </summary>
        /// <returns>FALSE IF NOT CONNECTED ELSE TRUE</returns>
        public bool isClientConnected(TcpClient ClientSocket)
        {
            IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
            TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();
            foreach (TcpConnectionInformation c in tcpConnections)
            {
                TcpState stateOfConnection = c.State;

                if (c.LocalEndPoint.Equals(ClientSocket.Client.LocalEndPoint) && c.RemoteEndPoint.Equals(ClientSocket.Client.RemoteEndPoint))
                {
                    if (stateOfConnection == TcpState.Established)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }

                }
            }
            return false;
        }

    }
}